/*
 * Copyright 2006-2007 S W McIlroy
 * Licensed  under the  Apache License,  Version 2.0  (the "License");
 * you may not use  this file  except in  compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed  under the  License is distributed on an "AS IS" BASIS,
 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 * implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.mockfu;

import java.util.ArrayList;
import java.util.List;

class ExpectationSequence implements Expectable
{

    private final List<Expectation> sequence = new ArrayList<Expectation>();
    private int index = 0;

    public synchronized void add( Expectation expectation )
    {
        sequence.add( expectation );
    }

    public boolean isAllows()
    {
        int i = 0;
        synchronized( this )
        {
            i = index;
        }

        for( ; i < sequence.size() ; i++ )
        {
            Expectation next = sequence.get( i );
            if (!next.isAllows())
                return false;
        }

        return true;
    }

    public Object match( InvocationContext context )
    {
        /*
         * this is only thread-safe enough not to throw random exceptions when used concurrently;
         * the only correct concurrent behavior would be to synchronize the entire call, but that
         * would create liveness issues with long-lived stubs; this is ok, since when multiple
         * threads match within a sequence the results are unpredictable anyway
         */
        int i = 0;
        synchronized( this )
        {
            i = index;
        }

        for( ; i < sequence.size() ; i++ )
        {
            Expectation current = sequence.get( i );
            Object result = current.match( context );

            if( result != NO_MATCH )
            {
                synchronized( this )
                {
                    index = i;
                    if( !current.canMatchMore() )
                        index++;
                }
                return result;
            }

            if( !current.verifies() )
                return NO_MATCH;
        }

        return NO_MATCH;
    }

    public synchronized int getMatchCount()
    {
        return sequence.get( index ).getMatchCount();
    }

    public synchronized void verify()
    {
        for( int i = index ; i < sequence.size() ; i++ )
        {
            sequence.get( i ).verify();
        }
    }

    public synchronized boolean stubWithIfPossible( Stub<?> stub, MethodFilter filter )
    {
        boolean stubbed = false;

        for( Expectable expectation : sequence )
            stubbed |= expectation.stubWithIfPossible( stub, filter );

        return stubbed;
    }

    public synchronized void reset()
    {
        index = 0;
        for( Expectation expectation : sequence )
        {
            expectation.reset();
        }
    }

}
